﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OseaCS
{
    public static class BdacH
    {
        /*****************************************************************************
        FILE:  bdac.h
        AUTHOR:	Patrick S. Hamilton
        REVISED:	9/25/2001
          ___________________________________________________________________________

        bdac.h: Beat detection and classification parameter definitions.
        Copywrite (C) 2001 Patrick S. Hamilton

        This file is free software; you can redistribute it and/or modify it under
        the terms of the GNU Library General Public License as published by the Free
        Software Foundation; either version 2 of the License, or (at your option) any
        later version.

        This software is distributed in the hope that it will be useful, but WITHOUT ANY
        WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
        PARTICULAR PURPOSE.  See the GNU Library General Public License for more
        details.

        You should have received a copy of the GNU Library General Public License along
        with this library; if not, write to the Free Software Foundation, Inc., 59
        Temple Place - Suite 330, Boston, MA 02111-1307, USA.

        You may contact the author by e-mail (pat@eplimited.edu) or postal mail
        (Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville,
        MA 02143 USA).  For updates to this software, please visit our website
        (http://www.eplimited.com).
        ******************************************************************************/

        public static int BEAT_SAMPLE_RATE = 100;
        public static double BEAT_MS_PER_SAMPLE = ((double) 1000/ (double) BEAT_SAMPLE_RATE);

        public static int BEAT_MS10	= ((int) (10/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS20 = ((int) (20/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS40 = ((int) (40/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS50 = ((int) (50/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS60 = ((int) (60/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS70 = ((int) (70/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS80 = ((int) (80/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS90 = ((int) (90/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS100 = ((int) (100/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS110 = ((int) (110/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS130 = ((int) (130/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS140 = ((int) (140/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS150 = ((int) (150/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS250 = ((int) (250/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS280 = ((int) (280/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS300 = ((int) (300/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS350 = ((int) (350/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS400 = ((int) (400/BEAT_MS_PER_SAMPLE + 0.5));
        public static int BEAT_MS1000 = BEAT_SAMPLE_RATE;

        public static int BEATLGTH = BEAT_MS1000;
        public static int MAXTYPES = 8;
        public static int FIDMARK = BEAT_MS400;
    }

    public static class Bdac
    {
        /*****************************************************************************
        FILE:  bdac.cpp
        AUTHOR:	Patrick S. Hamilton
        REVISED:	5/13/2002
          ___________________________________________________________________________

        bdac.cpp: Beat Detection And Classification
        Copywrite (C) 2001 Patrick S. Hamilton

        This file is free software; you can redistribute it and/or modify it under
        the terms of the GNU Library General Public License as published by the Free
        Software Foundation; either version 2 of the License, or (at your option) any
        later version.

        This software is distributed in the hope that it will be useful, but WITHOUT ANY
        WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
        PARTICULAR PURPOSE.  See the GNU Library General Public License for more
        details.

        You should have received a copy of the GNU Library General Public License along
        with this library; if not, write to the Free Software Foundation, Inc., 59
        Temple Place - Suite 330, Boston, MA 02111-1307, USA.

        You may contact the author by e-mail (pat@eplimited.edu) or postal mail
        (Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville,
        MA 02143 USA).  For updates to this software, please visit our website
        (http://www.eplimited.com).
          __________________________________________________________________________

        bdac.cpp contains functions for handling Beat Detection And Classification.
        The primary function calls a qrs detector.  When a beat is detected it waits
        until a sufficient number of samples from the beat have occurred.  When the
        beat is ready, BeatDetectAndClassify passes the beat and the timing
        information on to the functions that actually classify the beat.

        Functions in bdac.cpp require functions in the following files:
		        qrsfilt.cpp
		        qrsdet.cpp
		        classify.cpp
		        rythmchk.cpp
		        noisechk.cpp
		        analbeat.cpp
		        match.cpp
		        postclas.cpp

         __________________________________________________________________________

	        Revisions:
		        5/13/02:
			        Encapsulated down sampling from input stream to beat template in
			        the function DownSampleBeat.

			        Constants related to time are derived from SAMPLE_RATE in qrsdet
                 and BEAT_SAMPLE_RATE in bcac.h.

        *******************************************************************************/
        //#include "qrsdet.h"	// For base SAMPLE_RATE
        //#include "bdac.h"

        public static int ECG_BUFFER_LENGTH = 1000;	// Should be long enough for a beat
                                                    // plus extra space to accommodate
                                                    // the maximum detection delay.
        public static int BEAT_QUE_LENGTH = 10;		// Length of que for beats awaiting
                                                    // classification.  Because of
                                                    // detection delays, Multiple beats
                                                    // can occur before there is enough data
                                                    // to classify the first beat in the que.

        //// Internal function prototypes.
        //void DownSampleBeat(int *beatOut, int *beatIn) ;
        //// External function prototypes.
        //int QRSDet( int datum, int init ) ;
        //int NoiseCheck(int datum, int delay, int RR, int beatBegin, int beatEnd) ;
        //int Classify(int *newBeat,int rr, int noiseLevel, int *beatMatch, int *fidAdj, int init) ;
        //int GetDominantType(void) ;
        //int GetBeatEnd(int type) ;
        //int GetBeatBegin(int type) ;
        //int gcd(int x, int y) ;
        //// Global Variables

        public static int[] ECGBuffer = new int[ECG_BUFFER_LENGTH];
        public static int ECGBufferIndex = 0;  // Circular data buffer.
        public static int[] BeatBuffer = new int[BdacH.BEATLGTH];
        public static int[] BeatQue = new int[BEAT_QUE_LENGTH];
        public static int BeatQueCount = 0; // Buffer of detection delays.
        public static int RRCount = 0;
        public static int InitBeatFlag = 1;

        /******************************************************************************
	        ResetBDAC() resets static variables required for beat detection and
	        classification.
        *******************************************************************************/

        unsafe public static void ResetBDAC()
	    {
            int dummy;
            QrsDet.QRSDet(0, 1); // Reset the qrs detector
            RRCount = 0;
            classify.Classify(BeatBuffer, 0, 0, &dummy, &dummy, 1);
            InitBeatFlag = 1;
            BeatQueCount = 0;	// Flush the beat que.
	    }


        /*****************************************************************************
        Syntax:
	        int BeatDetectAndClassify(int ecgSample, int *beatType, *beatMatch) ;

        Description:
	        BeatDetectAndClassify() implements a beat detector and classifier.
	        ECG samples are passed into BeatDetectAndClassify() one sample at a
	        time.  BeatDetectAndClassify has been designed for a sample rate of
	        200 Hz.  When a beat has been detected and classified the detection
	        delay is returned and the beat classification is returned through the
	        pointer *beatType.  For use in debugging, the number of the template
           that the beat was matched to is returned in via *beatMatch.

        Returns
	        BeatDetectAndClassify() returns 0 if no new beat has been detected and
	        classified.  If a beat has been classified, BeatDetectAndClassify returns
	        the number of samples since the approximate location of the R-wave.

        ****************************************************************************/

        unsafe public static int BeatDetectAndClassify(int ecgSample, ref int beatType, ref int beatMatch)
        {
            int detectDelay, rr, i, j;
            int noiseEst = 0, beatBegin, beatEnd;
            int domType;
            int fidAdj;
            int[] tempBeat = new int[(int)(QrsDetH.SAMPLE_RATE/BdacH.BEAT_SAMPLE_RATE)*BdacH.BEATLGTH];
            
            // Store new sample in the circular buffer.

            ECGBuffer[ECGBufferIndex] = ecgSample;
            if (++ECGBufferIndex == Bdac.ECG_BUFFER_LENGTH)
                ECGBufferIndex = 0;

            // Increment RRInterval count.

            ++RRCount;

            // Increment detection delays for any beats in the que.

            for (i = 0; i < BeatQueCount; ++i)
                ++BeatQue[i];

            // Run the sample through the QRS detector.

            detectDelay = QrsDet.QRSDet(ecgSample, 0);
            if (detectDelay != 0)
            {
                BeatQue[BeatQueCount] = detectDelay;
                ++BeatQueCount;
            }

            // Return if no beat is ready for classification.

            if ((BeatQue[0] < (BdacH.BEATLGTH - BdacH.FIDMARK) * (QrsDetH.SAMPLE_RATE / BdacH.BEAT_SAMPLE_RATE))
                || (BeatQueCount == 0))
            {
                NoiseChk.NoiseCheck(ecgSample, 0, rr, beatBegin, beatEnd);	// Update noise check buffer
                return 0;
            }

            // Otherwise classify the beat at the head of the que.

            rr = RRCount - BeatQue[0];	// Calculate the R-to-R interval
            detectDelay = RRCount = BeatQue[0];

            // Estimate low frequency noise in the beat.
            // Might want to move this into classify().

            domType = Match.GetDominantType();
            if (domType == -1)
            {
                beatBegin = QrsDetH.MS250;
                beatEnd = QrsDetH.MS300;
            }
            else
            {
                beatBegin = (QrsDetH.SAMPLE_RATE / BdacH.BEAT_SAMPLE_RATE) * (BdacH.FIDMARK - Match.GetBeatBegin(domType));
                beatEnd = (QrsDetH.SAMPLE_RATE / BdacH.BEAT_SAMPLE_RATE) * (Match.GetBeatEnd(domType) - BdacH.FIDMARK);
            }
            noiseEst = NoiseChk.NoiseCheck(ecgSample, detectDelay, rr, beatBegin, beatEnd);

            // Copy the beat from the circular buffer to the beat buffer
            // and reduce the sample rate by averageing pairs of data
            // points.

            j = ECGBufferIndex - detectDelay - (QrsDetH.SAMPLE_RATE / BdacH.BEAT_SAMPLE_RATE) * BdacH.FIDMARK;
            if (j < 0) j += Bdac.ECG_BUFFER_LENGTH;

            for (i = 0; i < (QrsDetH.SAMPLE_RATE / BdacH.BEAT_SAMPLE_RATE) * BdacH.BEATLGTH; ++i)
            {
                tempBeat[i] = ECGBuffer[j];
                if (++j == Bdac.ECG_BUFFER_LENGTH)
                    j = 0;
            }

            DownSampleBeat(BeatBuffer, tempBeat);

            // Update the QUE.

            for (i = 0; i < BeatQueCount - 1; ++i)
                BeatQue[i] = BeatQue[i + 1];
            --BeatQueCount;


            // Skip the first beat.

            if (InitBeatFlag != 0)
            {
                InitBeatFlag = 0;
                beatType = 13;
                beatMatch = 0;
                fidAdj = 0;
            }
            else // Classify all other beats.
            {
                beatType = classify.Classify(BeatBuffer, rr, noiseEst, beatMatch, &fidAdj, 0);
                fidAdj *= QrsDetH.SAMPLE_RATE / BdacH.BEAT_SAMPLE_RATE;
            }

            // Ignore detection if the classifier decides that this
            // was the trailing edge of a PVC.

            if (beatType == 100)
            {
                RRCount += rr;
                return (0);
            }

            // Limit the fiducial mark adjustment in case of problems with
            // beat onset and offset estimation.

            if (fidAdj > QrsDetH.MS80)
                fidAdj = QrsDetH.MS80;
            else if (fidAdj < -QrsDetH.MS80)
                fidAdj = -QrsDetH.MS80;

            return (detectDelay - fidAdj);
        }

        public static void DownSampleBeat(int[] beatOut, int[] beatIn)
	    {
            for (int i = 0; i < BdacH.BEATLGTH; ++i)
                beatOut[i] = (beatIn[i << 1] + beatIn[(i << 1) + 1]) >> 1;
	    }

    }
}
